/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <journal/views/notebook_camera.h>

namespace journal { namespace view {

void notebook_camera::moveToPage(
    const std::shared_ptr<ipage_layout>& layout, 
    const size_t pageNumber, 
    bool keepY, 
    const b::float32 duration,
    const m::point2d& acceleration
    )
{
    m::point2d offsetXY = layout->calculateCameraOffsetXY(pageNumber);

    if (keepY)
    {
        offsetXY.y = _driver->camera()->offsetXY().y;
    }

    if (duration > 0.f)
    {
        b::float32 t = constants::general::currentTime();

        _offsetXYAnimation.set(t, duration,  _driver->camera()->offsetXY(), offsetXY, acceleration.x, acceleration.y);
        _scaleAnimation.set(t, duration, _driver->camera()->scale(), layout->scale(), acceleration.x, acceleration.y);
        _distanceAnimation.set(t, duration, _driver->camera()->offsetDistance(), layout->distance(), acceleration.x, acceleration.y);
        _anglesAnimation.set(t, duration, _driver->camera()->angles(), layout->angles(), acceleration.x, acceleration.y);
    }
    else
    {
        _driver->camera()->setOffsetXY(offsetXY);
        _driver->camera()->setAngles(layout->angles());
        _driver->camera()->setScale(layout->scale());
        _driver->camera()->setOffsetDistance(layout->distance());
        _offsetXYAnimation.cancel();
        _anglesAnimation.cancel();
        _scaleAnimation.cancel();
        _distanceAnimation.cancel();
    }
}

void notebook_camera::setAngles(
    const m::point3d& angles, 
    const b::float32 duration,
    const m::point2d& acceleration
    )
{
    b::float32 t = constants::general::currentTime();

    if (duration > 0.f)
    {
        if (_driver->camera()->angles() == angles)
        {
            // asked to angles to the current target angles... so nothing to do
            return;
        }
        else
        {
            // need to reset the animation (or set a new one)
            _anglesAnimation.set(t, duration, _driver->camera()->angles(), angles, acceleration.x, acceleration.y);
            _state = notebook_camera_state::animating;
        }
    }
    else
    {
        _driver->camera()->setAngles(angles);
        _anglesAnimation.cancel();
    }
}

void notebook_camera::setDistance(
    const b::float32 distance, 
    const b::float32 duration,
    const m::point2d& acceleration
    )
{
    b::float32 t = constants::general::currentTime();

    if (duration > 0.f)
    {
        if (_driver->camera()->offsetDistance() == distance)
        {
            // asked to distance to the current target distance... so nothing to do
            return;
        }
        else
        {
            // need to reset the animation (or set a new one)
            _distanceAnimation.set(t, duration, _driver->camera()->offsetDistance(), distance, acceleration.x, acceleration.y);
            _state = notebook_camera_state::animating;
        }
    }
    else
    {
        _driver->camera()->setOffsetDistance(distance);
        _distanceAnimation.cancel();
    }
}

void notebook_camera::setOffsetXY(
    const m::point2d& offsetXY, 
    const b::float32 duration, 
    const m::point2d& acceleration,
    const bool delta
    )
{
    b::float32 t = constants::general::currentTime();

    m::point2d finalOffsetXY;

    if (delta)
    {
        _offsetXYDelta = offsetXY;
        finalOffsetXY = m::point2d(_driver->camera()->offsetXY().x + _offsetXYDelta.x, _driver->camera()->offsetXY().y + _offsetXYDelta.y);
    }
    else
    {
        finalOffsetXY = m::point2d(offsetXY.x + _offsetXYDelta.x, offsetXY.y + _offsetXYDelta.y);
    }

    if (duration > 0.f)
    {
        if (_driver->camera()->offsetXY() == finalOffsetXY)
        {
            // asked to offsetXY to the current target offsetXY... so nothing to do
            return;
        }
        else
        {
            // need to reset the animation (or set a new one)
            _offsetXYAnimation.set(t, duration, _driver->camera()->offsetXY(), finalOffsetXY, acceleration.x, acceleration.y);
            _state = notebook_camera_state::animating;
        }
    }
    else
    {
        _driver->camera()->setOffsetXY(finalOffsetXY);
        _offsetXYAnimation.cancel();
    }
}

void notebook_camera::setScale(
    const b::float32 scale, 
    const b::float32 duration,
    const m::point2d& acceleration
    )
{
    b::float32 t = constants::general::currentTime();

    if (duration > 0.f)
    {
        // need to reset the animation (or set a new one)
        _scaleAnimation.set(
            t, 
            duration, 
            _driver->camera()->scale(), 
            scale, 
            acceleration.x, 
            acceleration.y
            );

        _state = notebook_camera_state::animating;
    }
    else
    {
        _driver->camera()->setScale(scale);
        _scaleAnimation.cancel();
    }
}


void notebook_camera::reset(
    const std::shared_ptr<ipage_layout>& layout, 
    const b::float32 duration,
    const m::point2d& acceleration
    )
{

    if (duration > 0.f)
    {
        b::float32 t = constants::general::currentTime();

        _scaleAnimation.set(t, duration, _driver->camera()->scale(), layout->scale(), acceleration.x, acceleration.y);
        _distanceAnimation.set(t, duration, _driver->camera()->offsetDistance(), layout->distance(), acceleration.x, acceleration.y);
        _anglesAnimation.set(t, duration, _driver->camera()->angles(), layout->angles(), acceleration.x, acceleration.y);
    }
    else
    {
        _driver->camera()->setAngles(layout->angles());
        _driver->camera()->setScale(layout->scale());
        _driver->camera()->setOffsetDistance(layout->distance());
        _anglesAnimation.cancel();
        _scaleAnimation.cancel();
        _distanceAnimation.cancel();
    }
}

void notebook_camera::onTick()
{
    _offsetXYAnimation.apply();
    _anglesAnimation.apply();
    _scaleAnimation.apply();
    _distanceAnimation.apply();
}

void notebook_camera::onUpdate(const int id)
{
    if (id == animation_id::offsetXY)
    {
        _driver->camera()->setOffsetXY(_offsetXYAnimation.current());
    }
    else if (id == animation_id::angles)
    {
        _driver->camera()->setAngles(_anglesAnimation.current());
    }
    else if (id == animation_id::distance)
    {
        _driver->camera()->setOffsetDistance(_distanceAnimation.current());
    }
    else if (id == animation_id::scale)
    {
        _driver->camera()->setScale(_scaleAnimation.current());
    }
}

void notebook_camera::onEnd(const int id)
{
    if (_offsetXYAnimation.isFinished() && 
        _scaleAnimation.isFinished() && 
        _distanceAnimation.isFinished() &&
        _anglesAnimation.isFinished())
    {
        _state = notebook_camera_state::idle;
    }
}

}} // namespace journal::view